サービスアカウントの権限を借用してアプリケーションのデフォルト認証情報を設定する

サービスアカウントの権限を借用してアプリケーションのデフォルト認証情報を設定する

Clock Icon2024.09.02

はじめに

データアナリティクス事業本部のkobayashiです。

Google Cloudのプロジェクトでサービスアカウントを発行しその権限でリソースを操作したい場面はよくあるかと思います。その際にサービスアカウントの権限借用をすることでサービスアカウントのキーを発行せずとも実現することができます。
今回は下記エントリを参考にアプリケーションのデフォルト認証情報(ADC)でも権限借用を行ってみたいと思います。

https://dev.classmethod.jp/articles/gcp-gcloud-command-service-account/

アプリケーションのデフォルト認証情報を設定する

サービスアカウントの作成とトークン作成者権限を追加する

BigQuery管理者のポリシーを付けたサービスアカウントとGoogleCloudStorage(GCS)用のストレージ管理者のポリシーを付けたサービスアカウントを作成して、Pythonスクリプトでバケット一覧を取得できるかを試してみます。

はじめにBigQuery用のサービスアカウントを作成します。

$ gcloud iam service-accounts create test-bq-admin
Created service account [test-bq-admin].
$ gcloud projects add-iam-policy-binding {プロジェクトID} \
	  --member="serviceAccount:test-bq-admin@{プロジェクトID}.iam.gserviceaccount.com" \
	  --role="roles/bigquery.admin"

次にGCS用のサービスアカウントを作成します。

$ gcloud iam service-accounts create test-storage-admin
Created service account [test-storage-admin].
$ gcloud projects add-iam-policy-binding {プロジェクトID} \
	  --member="serviceAccount:test-storage-admin@{プロジェクトID}.iam.gserviceaccount.com" \
	  --role="roles/storage.admin"

これでサービスアカウントは準備できたので後は借用をするプリンシパルにサービスアカウントトークン作成者の権限(roles/iam.serviceAccountTokenCreator)を付与します。自分で作成したプロジェクトでもプリンシパルにこれらのロールを付与する必要がありるので注意が必要です。

$ gcloud projects add-iam-policy-binding {プロジェクトID} \
    --member="user:ユーザーのメールアドレス" --role="roles/iam.serviceAccountTokenCreator"

これで事前準備は整ったのでサービスアカウントの権限を借用してADCを設定できるようになったので実際に権限借用してPythonスクリプトを実行してみます。

権限借用してPythonスクリプトを実行する

サービス アカウントの権限借用をしてADCを設定するには通常のgcloud auth application-default logiコマンドにオプションとして--impersonate-service-account SERVICE_ACCT_EMAILを付けるだけです。

権限借用後に実行するPythonスクリプトは下記のものです。

gcs_list_bucket.py
from pprint import pprint
from google.cloud import storage
import google.auth
import google.auth.transport.requests

def print_default_credentials_info():
    credentials, project = google.auth.default()
    print("credentials:")
    pprint(vars(credentials))
    print(f"project:{project}")

def print_buckets():
    client = storage.Client()
    print("Bucket情報:")
    for bucket in client.list_buckets():
        print(bucket.name)

# メイン処理
if __name__ == "__main__":
    print_default_credentials_info()
    print_buckets()

内容はprint_default_credentials_info()でADCの情報を出力し、print_buckets()でプロジェクトのGCSバケット一覧を出力する非常にシンプルなスクリプトにっています。

はじめにGCS用のストレージ管理者のポリシーを付けたサービスアカウントを借用するためADCを設定します。

$ gcloud auth application-default login --impersonate-service-account test-storage-admin@{プロジェクトID}.iam.gserviceaccount.com

スクリプト実行前にADCの内容を確認してみます。

$ cat ~/.config/gcloud/application_default_credentials.json
{
  "delegates": [],
  "service_account_impersonation_url": "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/test-storage-admin@{プロジェクトID}.iam.gserviceaccount.com:generateAccessToken",
  "source_credentials": {
    "account": "",
    "client_id": "xxxxxx-yyyyyyyyyyyy.apps.googleusercontent.com",
    "client_secret": "d-xxxxxxxxxxxxxxxxxxxxx",
    "refresh_token": "1//xxxxxxxxxxxxxxxxxxxxx-yyyyyyyyyyyyyyyyy",
    "type": "authorized_user",
    "universe_domain": "googleapis.com"
  },
  "type": "impersonated_service_account"
}

typeとしてimpersonated_service_accountが使われていることがわかります。
ではこの状態でPythonスクリプトを実行します。

$ python gcs_list_bucket.py
credentials:
{'_default_scopes': None,
 '_delegates': [],
 '_iam_endpoint_override': None,
 '_lifetime': 3600,
 '_quota_project_id': None,
 '_scopes': None,
 '_source_credentials': <google.oauth2.credentials.Credentials object at 0x10f823490>,
 '_target_principal': 'test-storage-admin@{プロジェクトID}.iam.gserviceaccount.com',
 '_target_scopes': None,
 'expiry': datetime.datetime(2024, 8, 31, 21, 56, 9, 380857),
 'token': None}
project:{プロジェクトID}
Bucket情報:
aaaaaaaa-bucket
bbbbbbbb-bucket
cccccccc-bucket

スクリプト実行時に認証ライブラリによってGCS用のストレージ管理者のポリシーを付けたサービスアカウントを借用した権限でスクリプトが実行されていることがわかります。またバケット情報も取得できています。

BigQuery管理者のポリシーを付けたサービスアカウントを借用してADCの中身を確認してみます。

$ gcloud auth application-default login --impersonate-service-account test-bq-admin@{プロジェクトID}.iam.gserviceaccount.com
$ cat ~/.config/gcloud/application_default_credentials.json
{
  "delegates": [],
  "service_account_impersonation_url": "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/test-bq-admin@{プロジェクトID}.iam.gserviceaccount.com:generateAccessToken",
  "source_credentials": {
    "account": "",
    "client_id": "xxxxxx-yyyyyyyyyyyy.apps.googleusercontent.com",
    "client_secret": "d-xxxxxxxxxxxxxxxxxxxxx",
    "refresh_token": "1//xxxxxxxxxxxxxxxxxxxxx-yyyyyyyyyyyyyyyyy",
    "type": "authorized_user",
    "universe_domain": "googleapis.com"
  },
  "type": "impersonated_service_account"
}

ではこの状態でPythonスクリプトを実行します。

$ credentials:
{'_default_scopes': None,
 '_delegates': [],
 '_iam_endpoint_override': None,
 '_lifetime': 3600,
 '_quota_project_id': None,
 '_scopes': None,
 '_source_credentials': <google.oauth2.credentials.Credentials object at 0x11a337ad0>,
 '_target_principal': 'test-bq-admin@{プロジェクトID}.iam.gserviceaccount.com',
 '_target_scopes': None,
 'expiry': datetime.datetime(2024, 8, 31, 21, 55, 11, 294264),
 'token': None}
project:{プロジェクトID}
Bucket情報:
Traceback (most recent call last):
  File "/xxxxxxxxx/gcs_list_bucket.py", line 25, in <module>
    print_buckets()
  File "/xxxxxxxxx/gcs_list_bucket.py", line 17, in print_buckets
    for bucket in client.list_buckets():
...
  File "/xxxxxxxxx/venv/lib/python3.11/site-packages/google/cloud/_http/__init__.py", line 494, in api_request
    raise exceptions.from_http_response(response)
google.api_core.exceptions.Forbidden: 403 GET https://storage.googleapis.com/storage/v1/b?project={プロジェクトID}&projection=noAcl&prettyPrint=false: test-bq-admin@{プロジェクトID}.iam.gserviceaccount.com does not have storage.buckets.list access to the Google Cloud project. Permission 'storage.buckets.list' denied on resource (or it may not exist).

BigQuery管理者のポリシーを付けたサービスアカウントでスクリプトが実行されていますが、借用したサービスアカウントに権限が足りないためエラーが出ることが確認できました。

一応サービスアカウントを借用しない状態のADCの中身を確認しておきます。

$  cat ~/.config/gcloud/application_default_credentials.json
{
  "account": "",
    "client_id": "xxxxxx-yyyyyyyyyyyy.apps.googleusercontent.com",
    "client_secret": "d-xxxxxxxxxxxxxxxxxxxxx",
  "quota_project_id": "{プロジェクトID}",
    "refresh_token": "1//xxxxxxxxxxxxxxxxxxxxx-yyyyyyyyyyyyyyyyy",
  "type": "authorized_user",
  "universe_domain": "googleapis.com"
}

typeがimpersonated_service_accountではなくauthorized_userであることがわかります。

まとめ

サービスアカウントの権限借用を使用してアプリケーションのデフォルト認証情報(ADC)を設定してみました。ADCの場合も簡単にサービスアカウントの権限を借用してスクリプトを実行できました。

最後まで読んで頂いてありがとうございました。

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.